package com.nariano.server.domain.engine.droolsimpl;

import java.util.Collection;
import java.util.LinkedList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.Assert;

import com.nariano.server.domain.entities.Area;
import com.nariano.server.domain.entities.Location;
import com.nariano.server.domain.entities.Population;
import com.nariano.server.domain.entities.User;
import com.nariano.server.domain.utils.Direction;
import com.nariano.server.publisher.ClientPublisher;
import com.nariano.server.publisher.events.AreaDiscovered;
import com.nariano.server.services.mapservice.MapService;

public class MoveFromAreaAction {
	
	private static final int MAX_POPULATION = 1;

	private static Random rand = new Random();
    
    @Autowired
    @Qualifier(value="mapService")
    private MapService mapService;
    
    @Autowired
	@Qualifier(value="clientPublisher")
    ClientPublisher clientPublisher;
    
    private Logger logger = Logger.getLogger(MoveFromAreaAction.class.getCanonicalName());
    
	
	public MoveFromAreaAction() {
	
	}

	public MoveFromAreaAction(MapService mapService) {
		this.mapService = mapService;
	}

	/**
	 * Moves out all population exceeding 1
	 * @param area
	 * @return
	 */
	public Collection<Area> moveFromArea(Area area) {
		
		Collection<Area> targetAreas = new LinkedList<Area>();
		for (Integer u : area.getUsers()) {
			for(int i=0; i<area.getPopulationCount()-1; i++) {
				Area targetArea = moveOut(area, u);
				if (targetArea!=null) {
					targetAreas.add(targetArea);
				}
			}
		}
		return targetAreas;
	}

	private Area moveOut(Area sourceArea, Integer user) {
		
		Direction dir = Direction.getRandomDirection(sourceArea.getLocation(), mapService.getBoundaries());
		Location targetLocation = dir.getRandomLocation(sourceArea.getLocation(), mapService.getBoundaries());
		Area targetArea = mapService.getArea(targetLocation);
		/*int prePopCount =  sourceArea.getPopulationCount() + targetArea.getPopulationCount();
		
		if (logger.isDebugEnabled()) {
			logger.debug("Pre: Source population " + sourceArea.getPopulation());
			logger.debug("Pre: Target population " + targetArea.getPopulation());
		}*/
		
		Population p = sourceArea.moveOutRandomPopulation(user);
		if (p==null) {
			return null; // none to move out
		}
			
		Assert.notNull(targetArea, "Failed to find target area from " + sourceArea.getLocation() + " at direction " + dir);
		
		AreaDiscovered discoveredArea = null; 
        if (targetArea.getPopulationCount()==0) {
        	discoveredArea = new AreaDiscovered(targetArea);
		}
		
		targetArea.addPopulation(p);
		mapService.updateAreas(sourceArea,targetArea);
		
		if (discoveredArea!=null) {
			clientPublisher.publish(discoveredArea);
		}
		
		if (logger.isLoggable(Level.FINE)) {
			logger.fine("Move one " + p.type().getName() + " from " 
						 + sourceArea.getLocation() + " to " + targetArea.getLocation());
			logger.fine("Source population " + sourceArea.getPopulationCount());
			logger.fine("Target population " + targetArea.getPopulationCount());
		}
		
		//int postPopCount =  sourceArea.getPopulationCount() + targetArea.getPopulationCount();
		//Assert.isTrue(prePopCount == postPopCount, String.format("%d, %d\n%s\n%s", prePopCount, postPopCount, sourceArea.toString(), targetArea.toString()));
		return targetArea;
		
	}

}
